//
//  buffer.h
//  medusa
//
//  Created by 郭王伟 on 13-4-6.
//  Copyright (c) 2013年 Bullets in a Burning Box, Inc. All rights reserved.
//

#ifndef __MEDUSA_BUFFER__
#define __MEDUSA_BUFFER__

#include "../MedusaMacros.h"
#include <string>

NS_MEDUSA_BEGIN

class Buffer {
    
protected:
    Buffer() {}
    virtual ~Buffer() {}
    
private:
    Buffer(const Buffer& obj) {}
    
    Buffer& operator = (const Buffer& obj) {
        return *this;
    }
    
public:
    
    virtual void Clear() = 0;
    
    virtual bool IsFull() const = 0;
    
    virtual bool IsEmpty() const = 0;
    
    virtual size_t Size() const = 0;
    
    virtual size_t ExpandSize() const = 0;
    
    virtual size_t DataSize() const = 0;
    
    virtual size_t FreeSize() const = 0;
    
    virtual size_t LineDataSize() const = 0;
    
    virtual size_t LineFreeSize() const = 0;
    
    // Byte
    virtual byte ReadByte() = 0;
    
    virtual void WriteByte(byte val) = 0;
    
    // boolean
    bool ReadBoolean() {
        return this->ReadByte() != 0;
    }
    
    void WriteBoolean(bool val) {
        this->WriteByte(((byte) (val ? 1 : 0)));
    }
    
    // Int8
    uint8_t ReadInt8() {
        return (uint8_t)this->ReadByte();
    }
    
    void WriteInt8(uint8_t val) {
        this->WriteByte((byte) val);
    }
    
    // Int16
    uint16_t ReadInt16LE() {
        uint16_t accum = 0;
        
        accum |= this->ReadByte();
		accum |= this->ReadByte() << 8;
        
		return accum;
    }
    
    uint16_t ReadInt16BE() {
        uint16_t accum = 0;
        
        accum |= this->ReadByte() << 8;
        accum |= this->ReadByte();
		
		return accum;
    }
    
    void WriteInt16LE(uint16_t val) {
        this->WriteByte((val) & 0xff);
        this->WriteByte((val>>8) & 0xff);
    }
    
    void WriteInt16BE(uint16_t val) {
        this->WriteByte((byte) (val>>8));
        this->WriteByte((byte) (val));
    }
    
    // Int32
    uint32_t ReadInt32LE() {
        uint32_t accum = 0;
        
        accum |= this->ReadByte();
		accum |= this->ReadByte()<< 8;
        accum |= this->ReadByte()<< 16;
        accum |= this->ReadByte()<< 24;
        
		return accum;
    }
    
    void WriteInt32LE(uint32_t val) {
        this->WriteByte((val) & 0xff);
        this->WriteByte((val>>8) & 0xff);
        this->WriteByte((val>>16) & 0xff);
        this->WriteByte((val>>24) & 0xff);
    }
    
    uint32_t ReadInt32BE() {
        uint32_t accum = 0;
        
        accum |= this->ReadByte() << 24;
        accum |= this->ReadByte() << 16;
        accum |= this->ReadByte() << 8;
        accum |= this->ReadByte();
		
		return accum;
    }
    
    void WriteInt32BE(uint32_t val) {
        this->WriteByte((val>>24) & 0xff);
        this->WriteByte((val>>16) & 0xff);
        this->WriteByte((val>>8) & 0xff);
        this->WriteByte((val) & 0xff);
    }
    
    // Int64
    uint64_t ReadInt64LE() {
        uint64_t accum = 0;
        
        accum |= (uint64_t)this->ReadByte();
		accum |= (uint64_t)this->ReadByte() << 8;
        accum |= (uint64_t)this->ReadByte() << 16;
        accum |= (uint64_t)this->ReadByte() << 24;
        accum |= (uint64_t)this->ReadByte() << 32;
        accum |= (uint64_t)this->ReadByte() << 40;
        accum |= (uint64_t)this->ReadByte() << 48;
        accum |= (uint64_t)this->ReadByte() << 56;
        
		return accum;
    }
    
    void WriteInt64LE(uint64_t val) {
        this->WriteByte((val) & 0xff);
        this->WriteByte((val>>8) & 0xff);
        this->WriteByte((val>>16) & 0xff);
        this->WriteByte((val>>24) & 0xff);
        this->WriteByte((val>>32) & 0xff);
        this->WriteByte((val>>40) & 0xff);
        this->WriteByte((val>>48) & 0xff);
        this->WriteByte((val>>56) & 0xff);
    }
    
    uint64_t ReadInt64BE() {
        uint64_t accum = 0;
        
        accum |= (uint64_t)this->ReadByte() << 56;
        accum |= (uint64_t)this->ReadByte() << 48;
        accum |= (uint64_t)this->ReadByte() << 40;
        accum |= (uint64_t)this->ReadByte() << 32;
        accum |= (uint64_t)this->ReadByte() << 24;
        accum |= (uint64_t)this->ReadByte() << 16;
        accum |= (uint64_t)this->ReadByte() << 8;
        accum |= (uint64_t)this->ReadByte();
		
		return accum;
    }
    
    void WriteInt64BE(uint64_t val) {
        this->WriteByte((val>>56) & 0xff);
        this->WriteByte((val>>48) & 0xff);
        this->WriteByte((val>>40) & 0xff);
        this->WriteByte((val>>32) & 0xff);
        this->WriteByte((val>>24) & 0xff);
        this->WriteByte((val>>16) & 0xff);
        this->WriteByte((val>>8) & 0xff);
        this->WriteByte((val) & 0xff);
    }
    
    
    // Float32
    float32_t ReadFloat32LE() {
        uint32_t ival = this->ReadInt32LE();
        float32_t val=0.0;
        ::memccpy(&val,&ival, 0, sizeof (float32_t));
        
        return val;
    }
    
    void WriteFloat32LE(float32_t val) {
        
        uint32_t ival = 0;
        ::memccpy(&ival, &val, 0, sizeof (float32_t));
        this->WriteInt32LE(ival);
    }
    
    float32_t ReadFloat32BE() {
        uint32_t ival = this->ReadInt32BE();
        float32_t val=0.0;
        ::memccpy(&val,&ival,0, sizeof val);
        
        return val;
    }
    
    void WriteFloat32BE(float32_t val) {
        uint32_t ival = 0;
        ::memcpy(&ival, &val, sizeof (float32_t));
        
        this->WriteInt32BE(ival);
    }
    /*
     static void swizzle(char* buf, size_t len) {
     char t;
     for (size_t i = 0; i < len / 2; ++i) {
     t = buf[i];
     buf[i] = buf[len - i - 1];
     buf[len - i - 1] = t;
     }
     }*/
    
    // Float64
    float64_t ReadFloat64LE() {
        uint64_t ival = this->ReadInt64LE();
        float64_t val=0.0;
        ::memccpy(&val,&ival, 0, sizeof (float64_t));
        
        return val;
    }
    
    void WriteFloat64LE(float64_t val) {
        uint64_t ival = 0;
        ::memccpy(&ival, &val, 0, sizeof (float64_t));
        this->WriteInt64LE(ival);
    }
    
    float64_t ReadFloat64BE() {
        uint64_t ival = this->ReadInt64BE();
        float64_t val=0.0;
        ::memccpy(&val,&ival,0, sizeof val);
        
        return val;
    }
    
    void WriteFloat64BE(float64_t val) {
        uint64_t ival = 0;
        ::memcpy(&ival, &val, sizeof (float64_t));
        
        this->WriteInt64BE(ival);
    }
    
    // Line
    
    std::string ReadLine(std::string lineendings) {
        size_t line_size = 1024;
        char* line = new char[line_size];
        size_t line_index = 0;

        while (true) {
            // 防御
            if (line_index == line_size) {
                size_t new_line_size = line_size + 1024;
                //byte* old_line = line;
                char* new_line = new char[new_line_size];
                ::memccpy(new_line, line, 0, line_index);
                line_size = new_line_size;
                delete []line;
                line = new_line;
            }
            // read
            line[line_index] = this->ReadByte();
            if(line_index+1 >=lineendings.size()) {
                if(strstr(line, lineendings.c_str())!=NULL)
                    break;
            }
            line_index++;
        }
        
        std::string ret(line, line_index + 1 - lineendings.size());
        delete []line;
        
        return ret;
    }
    
    std::string ReadLine() {
        return this->ReadLine("\n");
    }
    
    void WriteLine(std::string val, std::string lineendings) {
        this->WriteBytes(val.c_str(), val.size());
        this->WriteBytes(lineendings.c_str(), lineendings.size());
    }
    
    void WriteLine(std::string val) {
        this->WriteBytes(val.c_str(), val.size());
        this->WriteByte('\n');
    }
    
    // String
    std::string ReadString() {
        size_t size = this->ReadInt16BE();
        if(size>0) {
            char* buf = new char[size];
            this->ReadBytes(buf, size);
            std::string val(buf, size);
            delete [] buf;
            
            return val;
        }
        return "";
    }
    
    void WriteString() {
        this->WriteInt16BE(0);
    }
    
    void WriteString(const std::string& val) {
        this->WriteString(val.c_str());
    }
    
    void WriteString(const char* val) {
        if(val != NULL) {
            size_t size = strlen(val);
            this->WriteInt16BE(size);
            this->WriteBytes(val, size);
        } else
            this->WriteString();
    }
    
    //Bytes
    void ReadBytes(OUT void* bytes, size_t size) {
        byte* ptr = (byte*)bytes;
        for (int i = 0; i < size; i++) {
            ptr[i] = this->ReadByte();
        }
    }
    
    void WriteBytes(IN const void* bytes, size_t size) {
        byte* ptr = (byte*)bytes;
        for (int i = 0; i < size; i++) {
            this->WriteByte(ptr[i]);
        }
    }
    
    void ReadBytes(Buffer& dst, size_t size) {
        for (int i = 0; i < size; i++) {
            dst.WriteByte(this->ReadByte());
        }
    }
    
    void WriteBytes(Buffer& src, size_t size) {
        for (int i = 0; i < size; i++) {
            this->WriteByte(src.ReadByte());
        }
    }
};

NS_MEDUSA_END

#endif /* defined(__MEDUSA_BUFFER__) */
